#include "http.h"
#include "tcp.h"
#include "net.h"
#include "assert.h"
#include <sys/stat.h>
#include <time.h>

#define TCP_FIFO_SIZE 40

typedef struct http_fifo {
    tcp_connect_t* buffer[TCP_FIFO_SIZE]; // 循环队列
    uint8_t front, tail, count; // 队列头尾与元素总数
} http_fifo_t;

static http_fifo_t http_fifo_v;

/**
 * @brief 初始化http连接队列
 * 
 * @param fifo http连接队列
*/
static void http_fifo_init(http_fifo_t* fifo) {
    fifo->count = 0;
    fifo->front = 0;
    fifo->tail = 0; 
}

/**
 * @brief http连接入队
 * 
 * @param fifo http连接队列
 * @param tcp tcp连接
 * 
 * @return 入队成功返回0，失败返回-1
*/
static int http_fifo_in(http_fifo_t* fifo, tcp_connect_t* tcp) {
    if (fifo->count >= TCP_FIFO_SIZE) {
        return -1;
    }
    fifo->buffer[fifo->front] = tcp;
    fifo->front++;
    if (fifo->front >= TCP_FIFO_SIZE) {
        fifo->front = 0;
    }
    fifo->count++;
    return 0;
}

/**
 * @brief http连接出队
 * 
 * @param fifo http连接队列
 * 
 * @return tcp连接，若队列为空则返回NULL
*/
static tcp_connect_t* http_fifo_out(http_fifo_t* fifo) {
    if (fifo->count == 0) {
        return NULL;
    }
    tcp_connect_t* tcp = fifo->buffer[fifo->tail];
    fifo->tail++;
    if (fifo->tail >= TCP_FIFO_SIZE) {
        fifo->tail = 0;
    }
    fifo->count--;
    return tcp;
}

/**
 * @brief 从tcp连接读取一行数据
 * 
 * @param tcp tcp连接
 * @param buf 输出容器
 * @param size 最大读取数量
 * 
 * @return 读取到的一行数据的大小
*/
static size_t get_line(tcp_connect_t* tcp, char* buf, size_t size) {
    size_t i = 0;
    while (i < size) {
        char c;
        if (tcp_connect_read(tcp, (uint8_t*)&c, 1) > 0) {
            if (c == '\n') {
                break;
            }
            if (c == '\r')
                i++;
            if (c != '\n' && c != '\r') {
                buf[i] = c;
                i++;
            }
        }
        net_poll();
    }
    buf[i] = '\0';
    return i;
}

/**
 * @brief 发送http报文
 * 
 * @param tcp tcp连接
 * @param buf 发送数据
 * @param size 发送数据大小
 * 
 * @return 成功发送的数据量
*/
static size_t http_send(tcp_connect_t* tcp, const char* buf, size_t size) {
    size_t send = 0;
    while (send < size) {
        send += tcp_connect_write(tcp, (const uint8_t*)buf + send, size - send);
        net_poll();
    }
    return send;
}

/**
 * @brief 关闭http连接
 * 
 * @param tcp 待关闭的http连接对应的tcp连接
*/
static void close_http(tcp_connect_t* tcp) {
    tcp_connect_close(tcp);
    printf("http closed.\n");
}

/**
 * @brief 发送本地文件
 * 
 * @param tcp tcp连接
 * @param url 请求url  file:///path
*/
static void send_file(tcp_connect_t* tcp, const char* url) {
    FILE* file;
    uint32_t size;
    char file_path[255];
    char tx_buffer[1024];
    int len = strlen(url);

    /*
    解析url路径，查看是否是查看XHTTP_DOC_DIR目录下的文件
    如果不是，则发送404 NOT FOUND
    如果是，则用HTTP/1.0协议发送

    注意，本实验的WEB服务器网页存放在XHTTP_DOC_DIR目录中
    */

    char protocol[255];
    char *p = strstr(url, ":///");
    if (p == NULL) // url不合法
        return ;
    if (p - url != 4) // 一定不为file
        return ;
    memcpy(protocol, url, 4);
    if (memcmp(protocol, "file", 4)) // 不为file
        return ;

    p += 4;
    if (p - url >= len)
        return ;
    strcpy(file_path, p); 
    p = strstr(file_path, XHTTP_DOC_DIR);
    if (p == NULL) // 不是查看XHTTP_DOC_DIR目录下的文件
        return ;
    
    const char* connect = "Connection: close\r\n";
    const char* content_type = "Content-Type: text/html\r\n";
    const char* content_type_jpg = "Content-Type: image/jpg\r\n";
    const char* server = "SERVER: MYSERVER/200110322\r\n";
    char *header = tx_buffer;
    struct stat attr;
    stat(file_path, &attr);
    p = strstr(file_path, ".jpg"); // 判断文件类型
    // 状态行
    memcpy(header, HTTP_VERSION, 8);
    header += 8;
    *header++ = ' ';
    if (!strcmp(file_path, FOF_DIR)){
        memcpy(header, "404 Not Found\r\n", 15);
        header += 15;
    }
    else{
        memcpy(header, "200 OK\r\n", 8);
        header += 8;
    }
    if ((file = fopen(file_path, "rb")) == NULL){
        if ((file = fopen(FOF_DIR, "rb")) == NULL)
            return ;
        p = NULL;
        header -= 8;
        memcpy(header, "404 Not Found\r\n", 15);
        header += 15;
        stat(FOF_DIR, &attr);
    }
    // 首部行
    memcpy(header, connect, 19);
    header += 19;

    memcpy(header, "Date: ", 6);
    header += 6;
    time_t now;
    time(&now);
    char *temp = ctime(&now);
    memcpy(header, temp, strlen(temp) - 1);
    header += strlen(temp) - 1;
    memcpy(header, "\r\n", 2);
    header += 2;

    memcpy(header, server, 28);
    header += 28;

    memcpy(header, "Last-Modified: ", 15);
    header += 15;

    temp = ctime(&attr.st_mtime);
    memcpy(header, temp, strlen(temp) - 1);
    header += strlen(temp) - 1;
    memcpy(header, "\r\n", 2);
    header += 2;

    memcpy(header, "Content-Length: ", 16);
    header += 16;
    fseek(file, 0, SEEK_END);// 移动文件指针到文件末尾
	int file_size = ftell(file);	// 获取此时偏移值，即文件大小
    fseek(file, 0, SEEK_SET);
    header += sprintf(header, "%d\r\n", file_size);

    if (p == NULL){
        memcpy(header, content_type, 25);
        header += 25;
    }
    else{
        memcpy(header, content_type_jpg, 25);
        header += 25;
    }
    memcpy(header, "\r\n", 2);
    header += 2;

    char buf[512];
    size_t header_len = header - tx_buffer;
    if (size = fread(buf, 1, 512, file)){
        memcpy(header, buf, size);
        http_send(tcp, tx_buffer, size + header_len);
    }
    while (size = fread(tx_buffer, 1, 1024, file))
        http_send(tcp, tx_buffer, size);
        
    fclose(file);
}

static void http_handler(tcp_connect_t* tcp, connect_state_t state) {
    if (state == TCP_CONN_CONNECTED) {
        http_fifo_in(&http_fifo_v, tcp);
        printf("http conntected.\n");
    } else if (state == TCP_CONN_DATA_RECV) {
    } else if (state == TCP_CONN_CLOSED) {
        printf("http closed.\n");
    } else {
        assert(0);
    }
}


// 在端口上创建服务器。

int http_server_open(uint16_t port) {
    if (!tcp_open(port, http_handler)) {
        return -1;
    }
    http_fifo_init(&http_fifo_v);
    return 0;
}

// 从FIFO取出请求并处理。新的HTTP请求时会发送到FIFO中等待处理。

void http_server_run(void) {
    tcp_connect_t* tcp;
    char url_path[255];
    char rx_buffer[1024];

    while ((tcp = http_fifo_out(&http_fifo_v)) != NULL) {
        char* c = rx_buffer;

        /*
        1、调用get_line从rx_buffer中获取一行数据，如果没有数据，则调用close_http关闭tcp，并继续循环
        */

        if (!get_line(tcp, c, 1024)){
            close_http(tcp);
            continue;
        }
            
        /*
        2、检查是否有GET请求，如果没有，则调用close_http关闭tcp，并继续循环
        */

        if (strstr(c, "GET") == NULL){
            close_http(tcp);
            continue;
        }

        /*
        3、解析GET请求的路径，注意跳过空格，找到GET请求的文件，调用send_file发送文件
        */
        c = strstr(rx_buffer, "/");
        if (c == NULL){
            close_http(tcp);
            continue;
        }
        char *st = c;
        char *ed = strstr(c, " ");
        if (ed == NULL){
            close_http(tcp);
            continue;
        }
        memcpy(url_path, "file:///", 8);
        memcpy(&url_path[8], XHTTP_DOC_DIR, 11);
        if (ed - st > 1){
            memcpy(&url_path[19], st, ed - st);
            url_path[19 + ed - st] = 0;
        }
        else
            memcpy(&url_path[19], "/index.html", 12);
        send_file(tcp, url_path);

        /*
        4、调用close_http关掉连接
        */

        close_http(tcp);
        printf("!! final close\n");
    }
}
